#include <stdlib.h>

#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>

#include <stb_image.h>

#include "wave.h"
#include "parameters.h"
#include "id3.h"
#include "font.h"
#include "curve.h"
#include "sdl_data.h"
#include "dpi.h"
#include "music.h"
#include "kill.h"
#include "directory.h"
#include "particle.h"

static int render_width;
static int render_height;

static GLuint album_tex = 0;

static void initialize_opengl() {
	glEnable(GL_TEXTURE_2D);

	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	glViewport(0, 0, render_width, dpi_yi(render_height));
	
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0, render_width, 0, dpi_yi(render_height), -10, 10);

	glMatrixMode(GL_MODELVIEW);

	glClearColor(0.f, 0.f, 0.f, 0.f);

	glGenTextures(1, &album_tex);
}

static particle_system particles_left;
static particle_system particles_right;

void cleanup_particles() {
	particle_destroy(&particles_left);
	particle_destroy(&particles_right);
}

void render_initialize(int width, int height) {
	render_width = width;
	render_height = height;
	initialize_opengl();

	particles_left = particle_init(500, 6);
	particles_right = particle_init(500, 6);
	kill_push(cleanup_particles);
}

extern void process(unsigned char *data, int size, char *str_fallback, float *lr, float *lg, float *lb, float *rr, float *rg, float *rb, float *br, float *bg, float *bb, float *fr, float *fb, float *fg);
static float left_r, left_g, left_b;
static float right_r, right_g, right_b;
static float back_r, back_g, back_b;
static float front_r, front_g, front_b;

static float background_transparency;
static float album_transparency;
static float album_art_size;

void load_album_art(int length, char *data, const char *filename) {
	if (!data) {
		if (album_tex) glDeleteTextures(1, &album_tex);
		album_tex = 0;

		process(NULL, 0, filename, &left_r, &left_g, &left_b, &right_r, &right_g, &right_b, &back_r, &back_g, &back_b, &front_r, &front_b, &front_g);
		return;
	}

	int width;
	int height;
	int channels;
	stbi_set_flip_vertically_on_load(1);
	unsigned char *raw_data = stbi_load_from_memory((unsigned char*)data, length, &width, &height, &channels, 4);

	album_art_size = 75.f;
	album_transparency = 0.f;

	if (album_tex) {
		glBindTexture(GL_TEXTURE_2D, album_tex);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, raw_data);

		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

		process(raw_data, width * height, filename, &left_r, &left_g, &left_b, &right_r, &right_g, &right_b, &back_r, &back_g, &back_b, &front_r, &front_b, &front_g);
	}

	stbi_image_free(raw_data);
}

static void waveform_left(float t, float *x, float *y) {
	*x = t * render_width / wave_points();
	*y = dpi_y(BOX_OFFSET + wave_y((int)t, 0));
}

static void waveform_right(float t, float *x, float *y) {
	*x = t * render_width / wave_points();
	*y = dpi_y(BOX_OFFSET + wave_y((int)t, 1));
}

#define TEXT_TICKS 800
#define END_TICKS 1000
#define RESET_TICKS 1200

#define PARTICLE_TICKS 14

static float text_transparency = 0.f;

void render_update() {
	int global_mouse_y;
	SDL_GetGlobalMouseState(NULL, &global_mouse_y);

	float transparency_target = (global_mouse_y < dpi_y(100)) ? 0.f : 1.f;
	if (music_paused()) transparency_target = 0.f;

	background_transparency += (transparency_target - background_transparency) * 0.09f;

	album_art_size += (IMAGE_SIZE - album_art_size) * 0.02f;
	album_transparency += (1.f - album_transparency) * 0.09f;

	static int text_tick = 700;
	++text_tick;

	float text_target = (text_tick > TEXT_TICKS) ? 1.f : 0.f;
	if (text_tick > END_TICKS) text_target = 0.f;
	float text_factor = text_target > 0.5f ? 0.02f : 0.08f;
	if (text_transparency > 0.9f) text_factor = 0.08f;
	text_transparency += (text_target - text_transparency) * text_factor;
	if (text_tick > RESET_TICKS) {
		text_tick = 0;
		id3_step_cycle();
	}

	particle_update(&particles_left);
	particle_update(&particles_right);

	static int particle_tick = 12;
	++particle_tick;
	if (particle_tick > PARTICLE_TICKS) {
		particle_push(&particles_left, waveform_left, 0, wave_points());
		particle_push(&particles_right, waveform_right, 0, wave_points());
		particle_tick = 0;
	}

	
}

void render() {
	render_update();

	glClear(GL_COLOR_BUFFER_BIT);

	glBindTexture(GL_TEXTURE_2D, 0);

	

	glBegin(GL_QUADS);
		glColor4f(0.0f, 0.f, 0.0f, 0.0f);
		glVertex2f(0.f, dpi_y(BOX_OFFSET - 20));
		glVertex2f(render_width, dpi_y(BOX_OFFSET - 20));

		glColor4f(back_r, back_g, back_b, 0.95f * background_transparency);
		glVertex2f(render_width, dpi_y(WINDOW_HEIGHT));
		glVertex2f(0.f, dpi_y(WINDOW_HEIGHT));
	glEnd();

	curve_color(left_r, left_g, left_b, background_transparency);
	curve_draw(waveform_left, 0, wave_points(), 1.f);

	curve_color(right_r, right_g, right_b, background_transparency);
	curve_draw(waveform_right, 0, wave_points(), 1.f);

	particle_render(&particles_left, left_r, left_g, left_b, background_transparency);
	particle_render(&particles_right, right_r, right_g, right_b, background_transparency);

	/*glBegin(GL_LINE_STRIP);
	
	for (int x = 0; x < render_width; ++x) {
		glColor4f(
			wave_impact(x, 0, wr),
			wave_impact(x, 0, wg),
			wave_impact(x, 0, wb),
			0.8f);
		glVertex2f(x, BOX_OFFSET + wave_y(x, 0));
	}
	glEnd();

	glBegin(GL_LINE_STRIP);
	
	for (int x = 0; x < render_width; ++x) {
		glColor4f(
			wave_impact(x, 0, br),
			wave_impact(x, 0, bg),
			wave_impact(x, 0, bb),
			0.8f);
		glVertex2f(x, BOX_OFFSET + wave_y(x, 1));
	}
	glEnd();*/

	if(album_tex) {
		glBindTexture(GL_TEXTURE_2D, album_tex);
		glColor4f(1.f, 1.f, 1.f, 0.8f * background_transparency * album_transparency);
		glBegin(GL_QUADS);
			glTexCoord2f(0.f, 0.f);
			glVertex2f(0.f, dpi_y(WINDOW_HEIGHT - album_art_size));

			glTexCoord2f(1.f, 0.f);
			glVertex2f(dpi_x(album_art_size), dpi_y(WINDOW_HEIGHT - album_art_size));

			glTexCoord2f(1.f, 1.f);
			glVertex2f(dpi_x(album_art_size), dpi_y(WINDOW_HEIGHT));

			glTexCoord2f(0.f, 1.f);
			glVertex2f(0.f, dpi_y(WINDOW_HEIGHT));
		glEnd();

		glBindTexture(GL_TEXTURE_2D, 0);
	}

	const char *id3_info = id3_cycle();
	
	if (id3_info) {
		float title_x = 0, title_y = 0;
		font_measure(id3_info, &title_x, &title_y);
		title_x = render_width - title_x - 10;
		float y = dpi_y(BOX_OFFSET - WAVEFORM_HEIGHT);
		title_y = y + (dpi_y(WAVEFORM_HEIGHT) - title_y) * 0.5f;

		glBegin(GL_QUAD_STRIP);
			glColor4f(0.0f, 0.f, 0.0f, 0.0f);
			glVertex2f(title_x - dpi_x(10), y);
			glVertex2f(render_width, y);

			glColor4f(back_r, back_g, back_b, 0.95f * background_transparency * text_transparency);
			glVertex2f(title_x - dpi_x(10), y + dpi_y(WAVEFORM_HEIGHT * 0.5f));
			glVertex2f(render_width, y + dpi_y(WAVEFORM_HEIGHT * 0.5f));
			
			glColor4f(0.f, 0.f, 0.f, 0.f);
			glVertex2f(title_x - dpi_x(10), y + dpi_y(WAVEFORM_HEIGHT));
			glVertex2f(render_width, y + dpi_y(WAVEFORM_HEIGHT));
			
		glEnd();

		glColor4f(left_r, left_g, left_b, background_transparency * text_transparency);
		font_draw(id3_info, title_x, title_y);
	}
}

GLuint img_play = 0, img_pause = 0, img_hide = 0, img_close = 0;

static void load_image(GLuint *target, const char *path) {
	glGenTextures(1, target);

	int width;
	int height;
	int channels;
	stbi_set_flip_vertically_on_load(1);
	unsigned char *raw_data = stbi_load(path, &width, &height, &channels, 4);

	glBindTexture(GL_TEXTURE_2D, *target);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, raw_data);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	glBindTexture(GL_TEXTURE_2D, 0);

	stbi_image_free(raw_data);
}

float control_panel_needed;

static float cp_text_y;

#define C_SIZE 30

char *app_title;

static void cleanup_app_title() { free(app_title); }

int render_init(char *playlist) {
	load_image(&img_play, "img/play.png");
	load_image(&img_pause, "img/pause.png");
	load_image(&img_hide, "img/hide.png");
	load_image(&img_close, "img/close.png");

	char *basic_title = dir_isolate_file(playlist);
	size_t basic_title_size = strlen(basic_title);
	size_t app_title_size = basic_title_size + strlen(APP_NAME) + strlen(" | ") + 1;
	app_title = malloc(sizeof(char) * (app_title_size));
	snprintf(app_title, app_title_size, "%s | %s", APP_NAME, basic_title);

	free(basic_title);
	kill_push(cleanup_app_title);

	float title_x;
	float title_y;
	font_measure(app_title, &title_x, &title_y);
	control_panel_needed = title_x + dpi_x(20 + C_SIZE);

	cp_text_y = 0.5f * (dpi_y(C_SIZE) - title_y);

	int width = control_panel_needed + 2 * dpi_x(C_SIZE);
	glViewport(0, 0, width, dpi_yi(C_SIZE));
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0, width, 0, dpi_y(C_SIZE), -10, 10);
	glMatrixMode(GL_MODELVIEW);

	return width;
}

static float play_pause_gray = 1.f;
static float hide_gray = 1.f;
static float close_gray = 1.f;
extern int pause_highlighted;
extern int hide_highlighted;
extern int close_highlighted;
void rcp_update() {

	float play_pause_target = pause_highlighted ? 0.7f : 1.f;
	float hide_target = hide_highlighted ? 0.7f : 1.f;
	float close_target = close_highlighted ? 0.7f : 1.f;
	play_pause_gray += (play_pause_target - play_pause_gray) * 0.2f;
	hide_gray += (hide_target - hide_gray) * 0.2f;
	close_gray += (close_target - close_gray) * 0.2f;
}

void render_control_panel() {
	float title_x = 100;
	float title_y = 50;
	//font_measure(APP_NAME, &title_x, &title_y);

	float needed_text_width = title_x + 20;
	float rectangle_width = needed_text_width + 2 * C_SIZE;

	glClear(GL_COLOR_BUFFER_BIT);

	glBindTexture(GL_TEXTURE_2D, 0);

	glColor4f(0, 0x9c/255.f, 0x7d/255.f, 0.9f);

	glBegin(GL_QUADS);
		glVertex2f(dpi_x(C_SIZE * 0.5f), dpi_y(C_SIZE * 0.1f));
		glVertex2f(dpi_x(C_SIZE * 0.5f), dpi_y(C_SIZE * 0.9f));
		glVertex2f(dpi_x(C_SIZE * 0.5f) + control_panel_needed, dpi_y(C_SIZE * 0.9f));
		glVertex2f(dpi_x(C_SIZE * 0.5f) + control_panel_needed, dpi_y(C_SIZE * 0.1f));
	glEnd();

	glColor4f(0x00/255.f,0xbe/255.f,0x98/255.f, 0.9f);
	glBegin(GL_QUADS);
		glVertex2f(dpi_x(C_SIZE * 0.5f), dpi_y(C_SIZE * 0.0f));
		glVertex2f(dpi_x(C_SIZE * 0.5f), dpi_y(C_SIZE * 0.1f));
		glVertex2f(dpi_x(C_SIZE * 0.5f) + control_panel_needed, dpi_y(C_SIZE * 0.1f));
		glVertex2f(dpi_x(C_SIZE * 0.5f) + control_panel_needed, dpi_y(C_SIZE * 0.0f));

		glVertex2f(dpi_x(C_SIZE * 0.5f), dpi_y(C_SIZE * 1.0f));
		glVertex2f(dpi_x(C_SIZE * 0.5f), dpi_y(C_SIZE * 0.9f));
		glVertex2f(dpi_x(C_SIZE * 0.5f) + control_panel_needed, dpi_y(C_SIZE * 0.9f));
		glVertex2f(dpi_x(C_SIZE * 0.5f) + control_panel_needed, dpi_y(C_SIZE * 1.0f));
	glEnd();

	glColor3f(1.f, 1.f, 1.f);
	font_draw(app_title, dpi_x(C_SIZE + 10), cp_text_y);

	glBindTexture(GL_TEXTURE_2D, music_paused() ? img_play : img_pause);
	glColor3f(play_pause_gray, play_pause_gray, play_pause_gray);

	glBegin(GL_QUADS);
		glTexCoord2f(0.f, 0.f);
		glVertex2f(0, 0);

		glTexCoord2f(0.f, 1.f);
		glVertex2f(0, dpi_y(C_SIZE));

		glTexCoord2f(1.f, 1.f);
		glVertex2f(dpi_x(C_SIZE), dpi_y(C_SIZE));

		glTexCoord2f(1.f, 0.f);
		glVertex2f(dpi_x(C_SIZE), 0.f);
	glEnd();

	glColor3f(hide_gray, hide_gray, hide_gray);
	glBindTexture(GL_TEXTURE_2D, img_hide);
	glBegin(GL_QUADS);
		glTexCoord2f(0.f, 0.f);
		glVertex2f(control_panel_needed, 0);

		glTexCoord2f(0.f, 1.f);
		glVertex2f(control_panel_needed, dpi_y(C_SIZE));

		glTexCoord2f(1.f, 1.f);
		glVertex2f(control_panel_needed + dpi_x(C_SIZE), dpi_y(C_SIZE));

		glTexCoord2f(1.f, 0.f);
		glVertex2f(control_panel_needed + dpi_x(C_SIZE), 0.f);
	glEnd();

	glColor3f(close_gray, close_gray, close_gray);
	glBindTexture(GL_TEXTURE_2D, img_close);
	glBegin(GL_QUADS);
		glTexCoord2f(0.f, 0.f);
		glVertex2f(control_panel_needed + dpi_x(C_SIZE), 0);

		glTexCoord2f(0.f, 1.f);
		glVertex2f(control_panel_needed + dpi_x(C_SIZE), dpi_y(C_SIZE));

		glTexCoord2f(1.f, 1.f);
		glVertex2f(control_panel_needed + dpi_x(C_SIZE * 2), dpi_y(C_SIZE));

		glTexCoord2f(1.f, 0.f);
		glVertex2f(control_panel_needed + dpi_x(C_SIZE * 2), 0.f);
	glEnd();

	rcp_update();
}